//
// Created by Immortal on 4/25/2021.
//

#ifndef RACOON_HTTP_H
#define RACOON_HTTP_H
#include "OpStr.h"
#include <iostream>
#include <algorithm>
#include <map>
#include <list>

//状态码
#define sc200   "HTTP/1.1 200 OK"
#define sc400   "HTTP/1.1 400 Bad Request"
#define sc404   "HTTP/1.1 404 Not Found"

//类型
#define CT_TEXT_HTML        "text/html"
#define CT_TEXT_PLAIN       "text/plain"
#define CT_TEXT_XML         "text/xml"
#define CT_IMAGE_GIF         "image/gif"
#define CT_IMAGE_JPEG        "image/jpeg"
#define CT_IMAGE_PNG         "image/png"

#define CT_APP_XML          "application/xml"
#define CT_APP_JSON         "application/json"
#define CT_APP_PDF          "application/pdf"
#define CT_APP_OCTET_STREAM "application/octet-stream"



class Http {
public:
    //错误码
    enum ERROR
    {
        //无错误
        ERROR_NO = 0,
        //请求类型错误
        ERROR_TYPE,
        //请求头错误
        ERROR_HEAD,
        //结尾错误
        ERROR_END,
        //字段错误
        ERROR_FIELD,
        //Cookie错误
        ERROR_COOKIE

    };

    //请求类型
    enum REQ_TYPE
    {
        REQ_NULL = 0,
        REQ_GET,
        REQ_POST
    };

public:
    //响应Cookie结构
    typedef struct _RES_COOKIE
    {
        std::string strName;
        std::string strValue;
        std::string strDomain;
        std::string strPath;
        std::string strMaxAge;
    }RES_COOKIE, *PRES_COOKIE;

public:
    Http();
    Http(const char* buf);
    ~Http();

    //**************************************************************************************
    // 通用相关
    //**************************************************************************************
private:
    //初始化
    void Init();

public:
    //获取错误码
    Http::ERROR getError();
    //读取
    bool Read(const char* buf);
    //获取文件大小
    int GetFileSize(const char* path);
    //获取响应头
    void GetResHead(std::string& strBuf, int nContentSize = -1);

    //**************************************************************************************
    //**************************************************************************************


    //**************************************************************************************
    // 请求相关
    //**************************************************************************************

private:
    //解析字段
    bool AnalysisField(const char* buf, int n);
    //提取请求(path) 路径(param) 后缀(suffix)
    bool ExtractPPS(const char* buf, int n);
    //提取后缀(suffix)
    bool ExtractSuffix();
    //提取Cookie
    bool ExtractCookie(const char* buf, int n);
    //提取内容长度
    bool ExtractContentSize(const char* buf, int n);

public:
    //获取请求类型
    Http::REQ_TYPE GetReqType();
    //获取请求类型，返回值为字串
    const char* GetReqTypeStr();
    //获取请求路径
    const char* GetReqPath();
    //获取请求后缀
    const char* GetReqSuffix();
    //获取请求字段
    const char* GetReqField(const char* pszKey);
    //获取请求Cookie
    const char* GetReqCookie(const char* pszName);
    //获取请求内容长度
    int GetReqContentSize();
    //设置请求内容地址
    void SetReqContentAddr(std::string& strAddr){m_strReqContent = strAddr;}
    //获取请求内容
    const std::string& GetReqContent(){return m_strReqContent;}

    //**************************************************************************************
    //**************************************************************************************


    //**************************************************************************************
    // 响应相关
    //**************************************************************************************
private:

public:
    //设置响应状态码
    void SetResStatusCode(const char* buf);
    //获取响应状态码
    const char* GetResStatusCode();
    //设置字段
    void SetResField(const char* key, const char* value);
    //设置内容类型
    void SetContentType(const char* buf);
    //设置Cookie
    void SetCookie(const char* name, const char* value, const char* domain = NULL, const char* path = NULL, const char* max_age = NULL);
    //设置内容
    void SetContent(const void* buf, int n);
    //追加内容
    void AppendContent(const void* buf, int n);
    //设置响应文件
    bool SetResFile(const char* path);
    //获取响应内容
    const char* GetResContent();
    //获取响应文件大小
    int GetResFileSize();
    //获取响应类型
    bool GetResType();
    //设置是否保持连接
    void SetKeepAlive(bool b);
    //获取是否保持连接
    bool GetKeepAlive();
    //**************************************************************************************
    //**************************************************************************************

public:
    void Print()
    {
        if(m_rt == REQ_NULL){
            std::cout << "请求类型: NULL" << std::endl;
        }
        else if(m_rt == REQ_GET){
            std::cout << "请求类型: GET" << std::endl;
        }
        else if(m_rt == REQ_POST){
            std::cout << "请求类型: POST" << std::endl;
        }

        std::cout << "请求路径: " << m_strReqPath << std::endl;
        std::cout << "请求后缀: " << m_strReqSuffix << std::endl;

        std::cout << "请求参数:" << std::endl;
        for(auto it : *m_pliReqParam)
        {
            std::cout << it.first << ":" << it.second << std::endl;
        }

        std::cout << "请求字段:" << std::endl;
        for(auto it : *m_pmapReqField)
        {
            std::cout << it.first << ": " << it.second << std::endl;
        }

        std::cout << "请求Cookie:" << std::endl;
        for(auto it : * m_pmapReqCookie)
        {
            std::cout << it.first << ": " << it.second << std::endl;
        }


    }

private:
    //错误码
    ERROR m_err;

    //**************************************************************************************
    // 请求相关
    //**************************************************************************************
    //请求类型
    REQ_TYPE m_rt;
    //请求路径
    std::string m_strReqPath;
    //请求后缀
    std::string m_strReqSuffix;
    //请求参数 - GET
    std::list<std::pair<std::string, std::string>>* m_pliReqParam;
    //请求字段
    std::map<std::string, std::string>* m_pmapReqField;
    //请求Cookie
    std::map<std::string, std::string>* m_pmapReqCookie;
    //请求内容长度 -- 请求头里显示的长度
    int m_nReqContentSize;
    //请求内容
    std::string& m_strReqContent;
    std::string m_strReqContent_t;
    //**************************************************************************************
    //**************************************************************************************


    //**************************************************************************************
    // 响应相关
    //**************************************************************************************
    const char* m_pResCode;
    //响应字段
    std::list<std::pair<std::string, std::string>>* m_pliResField;
    //响应Cookie
    std::list<_RES_COOKIE>* m_pliResCookie;
    //响应内容
    std::string m_strResContent;
    //响应文件大小
    int m_nResFileSize;
    //响应内容是否为数据， true表示数据，false表示文件
    bool m_bIsData;
    //是否保持连接
    bool m_bKeepAlive;


    //**************************************************************************************
    //**************************************************************************************

};


#endif //RACOON_HTTP_H
